import { Node, Collider2D, RigidBody2D, v2, v3, Vec2, Vec3, _decorator, Enum } from "cc";
import yyComponent from "../../Script/Common/yyComponent";
import GameConfig from "../../Script/GameSpecial/GameConfig";
import { EventType } from "../../Script/GameSpecial/GameEventType";
import { GlobalEnum } from "../../Script/GameSpecial/GlobalEnum";

const { ccclass, property } = _decorator;
const halfToRad = 0.5 * Math.PI / 180.0;

/**关卡中需要用于碰撞检测的物体的基类（3D游戏中适用于不需要考虑旋转的碰撞体） */
@ccclass
export default class LevelColler extends yyComponent {
    @property({
        type: Enum(GlobalEnum.CollerType),
        visible: true,
    })
    protected _collerType: GlobalEnum.CollerType = GlobalEnum.CollerType.barrier;
    /**碰撞体类型 */
    public get collerType() { return this._collerType; }

    protected initSub() {
        this.initAABB();
        this.initRigidNode();
    }
    protected resetSub() {
        this.resetAABB();
        this.resetDirtyFlags();
        this.resetRigidNode();
    }

    public unuse() {
        this.reset();
        this.offEvents();
        this.disableRigidNode();
    }

    public onDestroy() {
        this.offEvents();
        this.disableRigidNode();
    }

    //#region 属性变化标记
    /**属性变化标记 */
    protected dirtyFlags: GlobalEnum.DirtyType = GlobalEnum.DirtyType.none;
    /**读取指定变化标记 */
    protected getDirtyFlags(type: GlobalEnum.DirtyType): boolean {
        return (this.dirtyFlags & type) > 0;
    }
    /**设置指定变化标记 */
    protected setDirtyFlags(type: GlobalEnum.DirtyType) {
        this.dirtyFlags |= type;
    }
    /**清除指定变化标记 */
    protected clearDirtyFlags(type: GlobalEnum.DirtyType) {
        this.dirtyFlags &= (~type);
    }
    /**重置变化标记 */
    protected resetDirtyFlags() {
        this.dirtyFlags = GlobalEnum.DirtyType.all;
    }
    //#endregion

    //#region 自定义的碰撞范围
    /**碰撞范围 */
    @property({
        // type: Vec3,
        displayName: "最小点",
        tooltip: "碰撞范围长方体在XYZ轴上坐标值最小的顶点在节点中的局部坐标"
    })
    public minVert: Vec3 = v3();
    @property({
        // type: Vec3,
        displayName: "最大点",
        tooltip: "碰撞范围长方体在XYZ轴上坐标值最大的顶点在节点中的局部坐标",

    })
    public maxVert: Vec3 = v3();
    protected _aabb: AABB = null;
    protected _center: Vec3;
    protected initAABB() {
        this._aabb = {
            xMin: 0,
            xMax: 0,
            xScope: 0,
            xCenter: 0,
            yMin: 0,
            yMax: 0,
            yScope: 0,
            yCenter: 0,
            zMin: 0,
            zMax: 0,
            zScope: 0,
            zCenter: 0,
        };
        this._center = v3();
        this.aabbDirty = true;
    }
    protected resetAABB() {
        this.aabbDirty = true;
    }

    public setPosition(p: Vec3) {
        this.node.setPosition(p);
        this.aabbDirty = true;
    }
    public setScale(s: Vec3) {
        this.node.setScale(s);
        this.aabbDirty = true;
    }

    /**获取碰撞包围盒 */
    public getAABB() {
        this.processDirty();
        return this._aabb;
    }

    /**碰撞包围盒变化标志 */
    protected get aabbDirty(): boolean { return this.getDirtyFlags(GlobalEnum.DirtyType.aabb); }
    /**碰撞包围盒变化标志 */
    protected set aabbDirty(v: boolean) {
        if (v) {
            this.setDirtyFlags(GlobalEnum.DirtyType.aabb);
        } else {
            this.clearDirtyFlags(GlobalEnum.DirtyType.aabb);
        }
    }
    /**更新碰撞包围盒 */
    protected processDirty() {
        if (this.aabbDirty) {
            this.aabbDirty = false;

            this._aabb.xMin = this.minVert.x + this.node.position.x;
            this._aabb.xMax = this.maxVert.x + this.node.position.x;
            this._aabb.xScope = (this._aabb.xMax - this._aabb.xMin) * 0.5;

            this._aabb.yMin = this.minVert.y + this.node.position.y;
            this._aabb.yMax = this.maxVert.y + this.node.position.y;
            this._aabb.yScope = this._aabb.yMax - this._aabb.yMin;

            this._aabb.zMin = this.minVert.z + this.node.position.z;
            this._aabb.zMax = this.maxVert.z + this.node.position.z;
            this._aabb.zScope = (this._aabb.zMax - this._aabb.zMin) * 0.5;

            this._aabb.xCenter = this._aabb.xMin + this._aabb.xScope;
            this._aabb.yCenter = this._aabb.yMin + this._aabb.yScope * 0.5;
            this._aabb.zCenter = this._aabb.zMin + this._aabb.zScope;

            this._center.x = this._aabb.xCenter;
            this._center.y = this._aabb.yCenter;
            this._center.z = this._aabb.zCenter;

        }
    }

    /**检测两个长方体是否相交 */
    public static checkAABB(a1: AABB, a2: AABB): boolean {
        return a1.xMax >= a2.xMin && a1.xMin <= a2.xMax
            && a1.yMax >= a2.yMin && a1.yMin <= a2.yMax
            && a1.zMax >= a2.zMin && a1.zMin <= a2.zMax;
    }

    public checkAABB(a2: AABB) {
        let a1 = this.getAABB();
        return a1.xMax >= a2.xMin && a1.xMin <= a2.xMax
            && a1.yMax >= a2.yMin && a1.yMin <= a2.yMax
            && a1.zMax >= a2.zMin && a1.zMin <= a2.zMax;
    }

    protected getCrossPos(a1: AABB, a2: AABB) {
        let x1 = this.getMax(a1.xMin, a2.xMin);
        let x2 = this.getMin(a1.xMax, a2.xMax);
        let y1 = this.getMax(a1.yMin, a2.yMin);
        let y2 = this.getMin(a1.yMax, a2.yMax);
        return v2((x1 + x2) * 0.5, (y1 + y2) * 0.5);
    }
    protected getMin(a, b) {
        return a < b ? a : b;
    }
    protected getMax(a, b) {
        return a > b ? a : b;
    }
    //#endregion

    /************************************碰撞回调************************************/
    public onColled(coller: LevelColler) { }

    //#region 碰撞体替身
    /**用于2d物理引擎计算的2d物理刚体节点 */
    protected rigidNode: Node = null;
    /**碰撞体替身的刚体组件 */
    protected rigid: RigidBody2D = null;
    protected collider2d: Collider2D = null;
    /**用于缓存刚体的线速度 */
    protected cacheLinearVelocity: Vec2;
    protected initRigidNode() {
        this.cacheLinearVelocity = v2(0, 0);
        this.createRigidNode();
    }
    protected resetRigidNode() {
        this.cacheLinearVelocity.x = 0;
        this.cacheLinearVelocity.y = 0;
        this.rigid.linearVelocity = this.cacheLinearVelocity;
        this.rigid.angularVelocity = 0;
    }
    /**在2D物理世界生成一个碰撞体替身 */
    protected createRigidNode() { }
    protected destroyRigidNode() {
        if (!!this.rigidNode) {
            this.rigid = null;
            this.collider2d = null;
            this.rigidNode.removeFromParent();
            this.rigidNode.destroy();
            this.rigidNode = null;
        }
    }
    /**激活碰撞体替身 */
    public enableRigidNode() {
        this.createRigidNode();
        this.resetRigidNode();
        this.emit(EventType.LevelEvent.addRigidNode, this.rigidNode);
        let rate = GameConfig.GameRule.rate3to2;
        this.rigidNode.setPosition(this.node.position.x * rate, -this.node.position.z * rate, 0);
        this.rigidNode.angle = this.node.eulerAngles.y;
        this.rigidNode.setScale(this.node.scale);
    }
    /**禁用碰撞体替身 */
    public disableRigidNode() {
        if (!!this.rigidNode) {
            this.rigidNode.removeFromParent();
            this.resetRigidNode();
        }
    }
    /**将3D节点信息同步到2D碰撞体 */
    public syncModelToRigid() { }
    /**将碰撞体信息同步到3D节点 */
    public syncRigidToModel() {
        //角度
        if (!this.rigid.fixedRotation) {
            let radianY = this.rigidNode.angle * halfToRad;
            this.node.setRotation(0, Math.sin(radianY), 0, Math.cos(radianY));
        }
        //坐标
        if (this.rigid.linearVelocity.x != 0 || this.rigid.linearVelocity.y != 0) {
            let p = this.rigidNode.position;
            let r = GameConfig.GameRule.rate2to3;
            let x = p.x * r;
            let y = this.node.position.y;
            let z = -p.y * r;
            this.node.setPosition(x, y, z);
            this.aabbDirty = true;
        }
    }
    //#endregion
}

type AABB = {
    xMin: number,
    xMax: number,
    xScope: number,
    xCenter: number,
    yMin: number,
    yMax: number,
    yScope: number,
    yCenter: number,
    zMin: number,
    zMax: number,
    zScope: number,
    zCenter: number,
};
